"""
语言提取器基类

所有语言特定提取器的抽象基类。
"""

import os
import subprocess
import shutil
from abc import ABC, abstractmethod
from typing import Dict, List, Optional
from dataclasses import dataclass
from pathlib import Path

from core.utils_module.logger import ObfuscationLogger
from core.builtin_config_module.builtin_config import BuiltInConfig
from core.interfaces_module.extractor import SymbolInfo


@dataclass
class CompilerInfo:
    """编译器信息"""
    command: str  # 编译器命令
    path: Optional[str] = None  # 编译器路径
    version: Optional[str] = None  # 版本信息
    available: bool = False  # 是否可用


class LanguageExtractor(ABC):
    """
    语言提取器基类

    提供所有语言特定提取器的共同功能:
    - 编译器检测
    - SDK 路径查找
    - 批量处理
    - 错误处理
    """

    def __init__(self, config: BuiltInConfig, logger: ObfuscationLogger):
        """
        初始化语言提取器

        Args:
            config: 配置对象
            logger: 日志记录器
        """
        self.config = config
        self.logger = logger
        self.compiler_info: Optional[CompilerInfo] = None

        # 检测编译器
        self._detect_compiler()

    @abstractmethod
    def get_language_name(self) -> str:
        """
        获取语言名称

        Returns:
            语言名称（如 'swift', 'objc', 'java', 'kotlin'）
        """
        pass

    @abstractmethod
    def get_compiler_command(self) -> str:
        """
        获取编译器命令

        Returns:
            编译器命令（如 'swiftc', 'clang', 'javac'）
        """
        pass

    @abstractmethod
    def get_file_extensions(self) -> List[str]:
        """
        获取文件扩展名列表

        Returns:
            扩展名列表（如 ['.swift'], ['.m', '.h']）
        """
        pass

    @abstractmethod
    def extract_symbols_from_file(self, file_path: str) -> Dict:
        """
        从单个文件提取符号

        Args:
            file_path: 文件路径

        Returns:
            符号字典 {符号名: SymbolInfo}
        """
        pass

    @abstractmethod
    def extract_symbols_batch(self, files: List[str]) -> Dict:
        """
        批量提取符号

        Args:
            files: 文件路径列表

        Returns:
            合并后的符号字典
        """
        pass

    def _detect_compiler(self):
        """检测编译器可用性"""
        command = self.get_compiler_command()
        env_var = f"{command.upper()}_PATH"

        # 1. 检查环境变量
        compiler_path = os.environ.get(env_var)
        if compiler_path and os.path.isfile(compiler_path):
            self.compiler_info = CompilerInfo(
                command=command,
                path=compiler_path,
                available=True
            )
            self.logger.log_operation(
                "编译器检测",
                f"{self.get_language_name()}: 使用环境变量 {env_var}={compiler_path}"
            )
            return

        # 2. 检查系统路径
        compiler_path = shutil.which(command)
        if compiler_path:
            # 获取版本
            version = self._get_compiler_version(compiler_path)
            self.compiler_info = CompilerInfo(
                command=command,
                path=compiler_path,
                version=version,
                available=True
            )
            self.logger.log_operation(
                "编译器检测",
                f"{self.get_language_name()}: 找到编译器 {compiler_path} ({version})"
            )
            return

        # 3. 未找到
        self.compiler_info = CompilerInfo(
            command=command,
            available=False
        )
        self.logger.log_warning(
            f"{self.get_language_name()} 编译器未找到: {command}"
        )

    def _get_compiler_version(self, compiler_path: str) -> Optional[str]:
        """
        获取编译器版本

        Args:
            compiler_path: 编译器路径

        Returns:
            版本字符串
        """
        try:
            result = subprocess.run(
                [compiler_path, '--version'],
                capture_output=True,
                text=True,
                timeout=5
            )
            if result.returncode == 0:
                # 提取第一行
                first_line = result.stdout.split('\n')[0]
                return first_line.strip()
        except Exception:
            pass
        return None

    def is_available(self) -> bool:
        """
        检查提取器是否可用

        Returns:
            编译器是否可用
        """
        return self.compiler_info is not None and self.compiler_info.available

    def get_compiler_path(self) -> Optional[str]:
        """
        获取编译器路径

        Returns:
            编译器路径或 None
        """
        if self.compiler_info:
            return self.compiler_info.path
        return None

    def _run_compiler(self, args: List[str], timeout: int = 60) -> subprocess.CompletedProcess:
        """
        运行编译器命令

        Args:
            args: 命令参数
            timeout: 超时时间（秒）

        Returns:
            CompletedProcess 对象

        Raises:
            RuntimeError: 如果编译器不可用
        """
        if not self.is_available():
            raise RuntimeError(
                f"{self.get_language_name()} 编译器不可用: {self.get_compiler_command()}"
            )

        cmd = [self.compiler_info.path] + args

        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=timeout
            )
            return result
        except subprocess.TimeoutExpired as e:
            raise RuntimeError(f"编译器超时: {' '.join(cmd)}") from e
        except Exception as e:
            raise RuntimeError(f"编译器执行失败: {str(e)}") from e

    def _merge_symbols(self, target: Dict, source: Dict):
        """
        合并符号字典

        Args:
            target: 目标字典（会被修改）
            source: 源字典
        """
        for key, value in source.items():
            if key not in target:
                target[key] = value
            else:
                # 如果是列表，合并
                if isinstance(value, list):
                    if isinstance(target[key], list):
                        target[key].extend(value)
                    else:
                        target[key] = [target[key], *value]
                # 如果是字典，递归合并
                elif isinstance(value, dict):
                    if isinstance(target[key], dict):
                        self._merge_symbols(target[key], value)
                    else:
                        target[key] = value

    def _get_sdk_path(self, sdk_name: str) -> Optional[str]:
        """
        获取 SDK 路径（macOS/iOS）

        Args:
            sdk_name: SDK 名称（如 'macosx', 'iphoneos'）

        Returns:
            SDK 路径或 None
        """
        try:
            result = subprocess.run(
                ['xcrun', '--sdk', sdk_name, '--show-sdk-path'],
                capture_output=True,
                text=True,
                timeout=10
            )
            if result.returncode == 0:
                path = result.stdout.strip()
                if os.path.exists(path):
                    return path
        except Exception:
            pass
        return None

    def __repr__(self) -> str:
        """字符串表示"""
        return (
            f"{self.__class__.__name__}("
            f"language={self.get_language_name()}, "
            f"available={self.is_available()})"
        )
